#pragma once

#include <Windows.h>

#include "NonCopyable.h"
#include "TestAutoPtr.h"
#include "TestString.h"
#include "TestVector.h"

namespace TestFramework
{
    enum AccessType
    {
        READ_ONLY  = KEY_READ,
        WRITE_ONLY = KEY_WRITE,
        ALL_ACCESS = KEY_ALL_ACCESS,
    };

    struct KeyInfo
    {
        DWORD SubKeysCount;
        DWORD ValuesCount;
        DWORD SecurityDescrptor;
        FILETIME LastWrite;
    };

    class RegistryKey;
    class RegistryValue;

    class KeysEnumerator : NonCopyable
    {
    public:
        virtual void ProcessKey(RegistryKey& key) = 0;
    };

    class ValueEnumerator : NonCopyable
    {
    public:
        virtual void ProcessValue(RegistryValue& value) = 0;
    };
    
    class RegistryValue
    {
    public:
        RegistryValue(const HKEY& root, const TestWString& name);

        template <typename T>
        void Set(const T& value)
        {
            SetValue(value);
        }

        template <typename T>
        void Get(T& value) const
        {
            const std::size_t size = GetValueSize();
            if (size)
            {
                GetValue(value, size);
            }
        }

    private:
        int GetValueSize() const;

        void GetValue(TestVector<byte>& data, int size) const;
        void GetValue(DWORD& data, int size) const;
        void GetValue(TestWString& data, int size) const;
        void GetValue(long long& data, int size) const;
        void SetValue(const TestVector<byte>& data);
        void SetValue(const DWORD data);
        void SetValue(const TestWString& data);
        void SetValue(const long long data);

    private:
        const HKEY& Key;
        TestWString Name;
    };

    class RegistryKey
    {
    public:
        RegistryKey(HKEY root, int accessType = READ_ONLY);
        RegistryKey(HKEY root, const TestWString& path, int accessType = READ_ONLY);
        ~RegistryKey();

        RegistryKey operator[](const TestWString& name);
        HKEY operator()() const;

        KeyInfo GetInfo() const;

        RegistryKey OpenOrCreateKey(const TestWString& path);
        RegistryKey CreateKey(const TestWString& path);
        void Close();

        TestAutoPtr<RegistryValue> CreateValue(const TestWString& name) const;
        TestAutoPtr<RegistryValue> OpenValue(const TestWString& name) const;

        void DeleteValue(const TestWString& name) const;
        void DeleteKey(const TestWString& path) const;

        void EnumerateKeys(KeysEnumerator& processor) const;
        void EnumerateValues(ValueEnumerator& processor) const;

    private:
        TestWString Name;
        HKEY Key;
        int Access;
    };

    RegistryKey GetClassesRoot(int accessType = READ_ONLY);
    RegistryKey GetCurrentUser(int accessType = READ_ONLY);
    RegistryKey GetLocalMachine(int accessType = READ_ONLY);
    RegistryKey GetUsers(int accessType = READ_ONLY);
    RegistryKey GetCurrentConfig(int accessType = READ_ONLY);
}
